package com.heima.wemedia.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.apis.IScheduleClient;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.schedule.dto.Task;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.WmMaterial;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmNewsMaterial;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.AutoScanService;
import com.heima.wemedia.service.WmNewsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmNewsService wmNewsService;

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Autowired
    private WmMaterialMapper wmMaterialMapper;

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    @Autowired
    private AutoScanService autoScanService;

    @Override
    public ResponseResult findAll(WmNewsPageReqDto dto) {
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        Short status = dto.getStatus();
        String keyword = dto.getKeyword();
        Integer channelId = dto.getChannelId();
        Date endPubDate = dto.getEndPubDate();
        Date beginPubDate = dto.getBeginPubDate();

        Integer size = dto.getSize();
        Integer pageNum = dto.getPage();

        // 查询频道
        LambdaQueryWrapper<WmNews> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(channelId != null, WmNews::getChannelId, channelId);

        // 查询发布时间范围
        wrapper.between(beginPubDate != null && endPubDate != null,
                WmNews::getPublishTime, beginPubDate, endPubDate);

        // 模糊查找关键词
        wrapper.like(StringUtils.isNotEmpty(keyword), WmNews::getTitle, keyword);

        // 查询状态
        wrapper.eq(status != null, WmNews::getStatus, status);

        // 根据用户查询
        wrapper.eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId());

        // 排序
        wrapper.orderByDesc(WmNews::getCreatedTime);

        Page<WmNews> page = new Page<>(pageNum, size);
        Page<WmNews> result = wmNewsMapper.selectPage(page, wrapper);

        // 组装返回值
        Long total = result.getTotal();
        PageResponseResult pageResult = new PageResponseResult(pageNum, size, total.intValue());
        pageResult.setData(result.getRecords());

        return pageResult;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public ResponseResult submit(WmNewsDto dto) {
        // 0. 入参判空
        if (dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }

        // 1. 从dto中提取需要的字段，写入到新闻表 wm_news 中
        WmNews wmNews = wmNewsDtoToWmNews(dto);
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR);
        }

        // 判断是否为草稿，前端传过来的 status 等于0时代表是草稿
        // 如果是草稿，则直接返回，不需要往下执行
        if (dto.getStatus() == 0) {
            return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
        }

        // 3. 提取内容图
        List<String> contentImages = getContentImages(dto.getContent());

        // 4. 插入内容图
        Boolean addMaterialResult = addMaterial(contentImages, 0, wmNews.getId());
        if (!addMaterialResult) {
            throw new RuntimeException("内容图添加失败");
        }

        // 5. 提取封面图
        List<String> coverImages = autoLayout(dto.getType(), dto.getImages(), contentImages);

        // 6. 将封面图插入到表中
        Boolean addCoverMaterialResult = addMaterial(coverImages, 1, wmNews.getId());
        if (!addCoverMaterialResult) {
            throw new RuntimeException("封面图添加失败");
        }

        // 7. 调用一下审核方法
        try {
            autoScanService.autoScanWmNews(wmNews.getId());
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    @Override
    public WmNews wmNewsDtoToWmNews(WmNewsDto wmNewsDto) {
        // 1. 入参判空
        if (wmNewsDto == null) {
            return null;
        }

        // 2. 创建一个wmNews对象，将dto中相同字段数据拷贝过来
        WmNews wmNews = new WmNews();
        BeanUtils.copyProperties(wmNewsDto, wmNews);

        // 3. 补全字段(userId,createdTime,type,images)
        // enable字段虽然dto里面有，但是前端没有传，所以需要额外维护一下
        // 后面三个字段不用管先放空(submitTime,articleId,reason)
        wmNews.setUserId(WmThreadLocalUtil.getUser().getId());
        wmNews.setCreatedTime(new Date());
        wmNews.setEnable((short) 1);

        // 4. dto的images是list类型，要转成wmNews的String类型
        String imagesString = StrUtil.join(",", wmNewsDto.getImages());
        wmNews.setImages(imagesString);

        // 5. 插入新闻表
        int insertResult = wmNewsMapper.insert(wmNews);
        if (insertResult != 1) {
            return null;
        }

        return wmNews;
    }

    @Override
    public List<String> getContentImages(String content) {
        // 1. 入参判空，如果为空直接返回一个空集合
        if (StrUtil.isBlank(content)) {
            return new ArrayList<>();
        }

        // 2. 声明一个结果集合，用来存储内容中的图片
        List<String> result = new ArrayList<>();

        // 3. 将内容字符串，转为List<Map>（JSON.parseArray）
        // 观察content的格式:
        // [{"type":"image","value":"rBEo4WOyRC6AYDTTAAArIxljsTE766.png"},{"type":"text","value":"第一行内容"}]
        List<Map> maps = JSON.parseArray(content, Map.class);
        if (CollectionUtil.isEmpty(maps)) {
            return new ArrayList<>();
        }

        // 4. 遍历前面得到的集合
        for (Map map : maps) {
            if (map == null) {
                continue;
            }
            // 每一组map包含两个字段，type,value
            // 4.1 判断type字段值是否为 image，如果是则提取value，加入到结果集合中
            Object type = map.get("type");
            if ("image".equals(type)) {

                Object value = map.get("value");
                if (value != null) {
                    result.add((String) value);
                }

            }
        }

        // 5. 返回结果集合
        return result;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean addMaterial(List<String> images, Integer type, Integer newsId) {
        // 传进来的图片集合，插入到素材表，和新闻素材关系表
        // 1. 入参判空
        if (CollectionUtil.isEmpty(images) || type == null || newsId == null) {
            return false;
        }

        // 2. 遍历图片集合
        for (String image : images) {
            if (StrUtil.isBlank(image)) {
                continue;
            }

            // 2.1 判断当前图片地址是否存在于素材表中(WmMaterial)
            LambdaQueryWrapper<WmMaterial> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(WmMaterial::getUrl, image);
            WmMaterial wmMaterial = wmMaterialMapper.selectOne(wrapper);

            // 2.2 声明变量，用来存储素材id，后面要用
            Integer materialId = null;

            // 2.3 如果为空，则将当前图片加入素材表
            // 字段(userId,url,isCollection,type,createdTime)
            if (wmMaterial == null) {
                WmMaterial wmMaterial1 = new WmMaterial();
                wmMaterial1.setUserId(WmThreadLocalUtil.getUser().getId());
                wmMaterial1.setCreatedTime(new Date());
                wmMaterial1.setIsCollection((short) 0);
                wmMaterial1.setType((short) 0);
                wmMaterial1.setUrl(image);

                int wmMaterial1Result = wmMaterialMapper.insert(wmMaterial1);
                if (wmMaterial1Result != 1) {
                    throw new RuntimeException("插入素材失败");
                }

                // 2.4 提取刚加入素材表的主键id
                materialId = wmMaterial1.getId();
            }
            // 2.5 如果不为空
            // 提取刚加入素材表的主键id
            else {
                materialId = wmMaterial.getId();
            }

            // 2.6 插入关系表
            // 字段(materialId,newsId,type,ord)
            // type 直接用这个方法的入参(0代表内容图，1代表封面图）
            // ord 设置为0即可
            WmNewsMaterial wmNewsMaterial = new WmNewsMaterial();
            wmNewsMaterial.setOrd((short) 0);
            wmNewsMaterial.setType(type.shortValue());
            wmNewsMaterial.setNewsId(newsId);
            wmNewsMaterial.setMaterialId(materialId);
            int newMaterialResult = wmNewsMaterialMapper.insert(wmNewsMaterial);
            if (newMaterialResult != 1) {
                throw new RuntimeException("新闻和素材关系添加失败");
            }
        }

        // 3. 返回true
        return true;
    }

    /**
     * 从内容图获取图片，填充到封面图上
     *
     * @param type          布局方式 -1自动布局
     * @param coverImage    前端传过来的封面图集合
     * @param contentImages 内容图集合
     * @return List<String> 封面图集合
     */
    @Override
    public List<String> autoLayout(short type, List<String> coverImage, List<String> contentImages) {
        // 1. 声明一个封面图集合
        List<String> result = new ArrayList<>();

        // 2. 判断是否需要自动布局
        // type等于-1的时候，代表需要自动布局
        // 如果是自动布局的话，就提取内容图来作为封面图使用
        if (type == -1) {
            // 2.1 获取内容图集合长度
            int size = contentImages.size();

            // 判断内容图的长度
            // 2.2 长度为 0 -> 无图 -> 返回空集合
            if (size == 0) {
                return new ArrayList<>();
            }
            // 2.3 长度为大于0并且小于3 -> 单图 -> 截取内容图集合的第一张图 -> 加入封面图集合
            else if (size < 3) {
                String url = contentImages.get(0);
                result.add(url);
            } else {
                // 2.4 长度大于3 -> 多图 -> 截取内容图集合的第1,2,3张图 -> 加入封面图集合
                List<String> urls = contentImages.subList(0, 3);
                result.addAll(urls);
            }
        } else {
            // 3. 如果不需要自动布局，则需要提取用户上传的封面图
            return contentImages;
        }

        // 4. 返回封面图集合
        return result;
    }

    @Autowired
    private IScheduleClient iScheduleClient;

    @Override
    public Boolean addToTask(Integer newsId, Long executeTime) {
        // 目标：这个方法主要是对延迟队列的addTask接口进行调用
        // 想要调用这个接口需要给这个接口传递一个task对象
        // 所以我要想办法把task中的数据找齐，组装起来

        // 1. 组装Task结构，额外处理一下executeTime字段
        Task task = new Task();
        task.setExecuteTime(executeTime);

        // 2. 查询新闻信息 wmNews，把新闻信息想办法设置到Task这个对象里面
        // task parameters变量是String类型
        // 所以我需要把wmNews -> String 存储进去
        WmNews wmNews = wmNewsMapper.selectById(newsId);
        if(wmNews != null) {
            task.setParameters(JSON.toJSONString(wmNews));
        }

        // 3. 把组装好的task结构，作为参数，调用Feign接口，实现任务添加
        ResponseResult responseResult = iScheduleClient.addTask(task);
        if(responseResult.getCode() != 200) {
            return false;
        }

        return true;
    }
}